/*
UP5: Ring

A user level program that demonstrates process communication.

A program should start a set of other processes, P1 to Pn.

The processes should be set up in a communications ring, where P1
sends messages to P2, etc. on to Pn.

The demonstration should send some messages around the ring and show
that they visit all processes along the way.

The user should only need to start a single process to start the ring
demo: the main program should start the other processes involved.

The main ring program should return to the shell when done setting up
the ring.

Timers for when to send messages should be set in such a way that it
is possible to observe the processes involved in the ring from the
shell.
*/

/*
POSIX Message Passing is used here to simulate the PORN-OS Message Passing
http://macboypro.com/blog/2009/06/29/posix-message-passing-in-linux-in-c/
ref.:http://www.cs.cf.ac.uk/Dave/C/node25.html
*/

/*
TODO: Write another program that spawns this, so that it is easier to
watch the processes
 */

#include <stdlib.h>
#include <stdio.h>

#include <string.h> //memcpy is used once

#include <sys/ipc.h> 
#include <sys/msg.h> 

#define NUM_PROCS 10
#define PARENT_MSG_QUEUE_ID 42

int main(int argc, char* argv){

  /*
    In POSIX Message Passing we have to create a mesage queue for
    every process, this is not the case in PORN-OS. To make this more
    PORN-OS like we need a map between pids and msqids, to provide
    pseudo pid addressing.
   
    1. element pid
    2. element msqid
  */
  int pid_msqid_map[NUM_PROCS][2];
  
  int length, result;
  int i;

  int parent_pid = getpid();


  //prepare a simple message
  struct {
    long priority;         //message priority
    int pid_msqid_map[NUM_PROCS][2]; //the mapping has to travel around with the message
    int index; //this index gets incremented as the message travels from proc to pro
    char payload[100];
  } msg;


  /*We need to remember the message length
   This is probably a little different in PORNOS
   */
   length = sizeof(msg) - sizeof(long); 


  //parent stores it's own mapping values
  pid_msqid_map[0][0] = getpid();
  pid_msqid_map[0][1] = msgget(PARENT_MSG_QUEUE_ID, 0600 | IPC_CREAT);


  /*
    Every child process creates it's message queue and the message queue of
    the neighbor it sends to.
    
    This will not be necessary in PORN-OS, because there the message
    queue is a part of the PCB per default.
  */

  //fork NUM_PROCS-1 times to create the child processes
  for(i=1; i < NUM_PROCS; i++){ 
   
    int fork_res = fork();
    if( fork_res == 0){ //child setup code
      
      /*the child creates the message queues that it needs
	and remembers it's mapping values.
	The mapping will be incomplete in the children
       */

      pid_msqid_map[i][0] = getpid();
      
      pid_msqid_map[i][1] = msgget(PARENT_MSG_QUEUE_ID+i, 0600 | IPC_CREAT);
      pid_msqid_map[i+1][1] = msgget(PARENT_MSG_QUEUE_ID+(i+1)%NUM_PROCS, 0600 | IPC_CREAT);      
      break; //make sure children don't fork themselves
    
    }else{ //parent setup code

      /*At first only the parent has all the children's mapping values
	I do this creation twice, because I am not sure which
	influence the fork has on it. Anyway this does not matter to
	PORNOS, because there is only one default message queue.
       */
      pid_msqid_map[i][0] = fork_res;
      pid_msqid_map[i][1] = msgget(PARENT_MSG_QUEUE_ID+i, 0600 | IPC_CREAT);

    }

  }

  if(getpid() == parent_pid) //correct parent's counter value
    i=0;
  
    
  /*Send the message around in a ring
   
    This is a bit tricky, because the processes find out who they have
    to send to from the message they receive.
   */
    
  //the parent inits the communication
  if(getpid() == parent_pid){
    
    //initialize message
    msg.priority = 1;
    memcpy(msg.pid_msqid_map,pid_msqid_map, sizeof(pid_msqid_map)); //there is an equivalent for memcpy in pornos
    msg.index = 1; //this message goes to the first receiver
    strcpy(msg.payload,"You suck! Tell the next process");
    
    //send the message to the next process
    result = msgsnd( pid_msqid_map[msg.index][1] , &msg, length, 0);
    printf("initial message: result=%d, length=%d\n\n", result, length);

  }else{

    //Every process receives a message and passes on a modified version of it
    
    /*recv Remark: In PORNOS we just won't have the first parameter,
      because it is clear which message queue belongs to which process
      and pids will be used for addressing.
    */
    result = msgrcv( pid_msqid_map[i][1], &msg, length, 1, 0);
    
    printf("pid=%d, i=%d received msg.index=%d, msg= \"%s\", result=%d (bytes received) \n", getpid(), i,msg.index, msg.payload, result);
    

    if(result != -1){ //keep on sending if receive succeeded

      //modify
      sleep(1); //output linearization
      msg.index = (msg.index + 1) % NUM_PROCS; //determine the next receiver
      
      if(msg.index == 0){
	      printf("pid=%d, I am annoyed of this game and therefore change the message\n", getpid(), msg.index);
	strcpy(msg.payload,"Fuck You");
      }

      
      printf("pid=%d, sending to proc# %d\n", getpid(), msg.index);
      
      /*send The mapping is used to find out the next receiver. In
	PORNOS this would just be the pid, which is element [0] in the mapping
      */
      result = msgsnd( msg.pid_msqid_map[msg.index][1], &msg, length, 0);
      printf("pid=%d, sending result is %d\n", getpid(), result);
    
    }
 
 }


  if(getpid() == parent_pid){ //the parent has to receive the last message

    result = msgrcv( msg.pid_msqid_map[0][1], &msg, length, 1, 0);
    printf("\n\npid=%d, i=%d (parent) received index=%d, msg= \"%s\", result=%d (bytes received)  \n", getpid(), i,msg.index, msg.payload, result);
    if(result != -1){
      printf("Ring Communication Succceeded!\n\n");
    }
    
    int k;
    for(k=1; k< NUM_PROCS; k++){
      wait();
    }

  }else{
  
    printf("pid=%d, i=%d my job is done\n",getpid(), i);
  
  }


  /*Remove the message queues
   
    It would be proper style to remove the message queues, but here we don't care, because:
    1. we have to do synchronization first, otherwise we destroy the buffer before it has been read
    2. it is not necessary in PORNOS

   */
  
  /*if(getpid() == parent_pid){
    int j;
    for(j=0; j<NUM_PROCS; j++){
      msgctl(pid_msqid_map[j][1], IPC_RMID, 0);
    }
  }else{
    msgctl(pid_msqid_map[i][1], IPC_RMID, 0);
    msgctl(pid_msqid_map[(i+1)%NUM_PROCS][1], IPC_RMID, 0);
    }*/

}
